package eu.webhib.extgui.controller.edit;

import eu.webhib.extgui.controller.intf.EditDataProvider;
import eu.webhib.extgui.controller.tree.TreeController;
import eu.webhib.extgui.controller.tree.ParentTreeController;
import eu.webhib.extgui.data.intf.GuiBaseEntity;
import eu.webhib.extgui.data.intf.GuiParentChildEntity;
import eu.webhib.extgui.data.service.intf.GuiParentChildService;
import eu.webhib.extgui.update.UpdateMngr;
import eu.webhib.frmwrk.jsf.base.EditBaseController;
import eu.webhib.frmwrk.utils.BeanUtils;
import javax.faces.event.ActionEvent;
import org.apache.log4j.Logger;

/**
 * Edit controller managing the details edit dialog for a data entity with parent/child relationship.
 * @param <T> type of entity with parent/child entity
 */
public class EditControllerParent<T extends GuiParentChildEntity<T>> extends EditBaseController {

   private static final Logger log = Logger.getLogger(EditControllerParent.class);
   /** holds data entities: delivers the entity to edit, save the changed one and delete an entity */
   private EditDataProvider<T> editDataProvider;
   /** Tree controller handling the selection in the parent tree component */
   private ParentTreeController<T> parentTreeController;
   /** the entity edited in the dialog window */
   private T entityToEdit = null;
   private T entityToEditOrig = null;
   /** the entity to be deleted */
   private T entityToDelete = null;
   /** controls the partial updating of view components after changing, adding or removing an entity */
   private UpdateMngr onChangeUpdateMngr;
   /** for update of parent tree component in details edit dialog */
   private static UpdateMngr parentTreeUpdate = new UpdateMngr("parentTree");
   /** Entity Service managing the persistent state of the entities */
   private GuiParentChildService<T> entityService;

   /**
    * @param editDataProvider provides the entity to edit, save and delete
    * @param entityService entity Service managing the persistent entities
    * @param onChangeUpdate updating of view components after changing, adding or removing an entity
    */
   public EditControllerParent(EditDataProvider<T> editDataProvider, GuiParentChildService<T> entityService, UpdateMngr onChangeUpdate) {
      this.editDataProvider = editDataProvider;
      this.onChangeUpdateMngr = onChangeUpdate;
      this.entityService = entityService;
   }

   /**
    * Called by view to get entity to edit in dialog
    * @return entity to be displayed and edited in edit dialog window
    */
   public GuiBaseEntity getItem() {
      return entityToEdit;
   }

   /**
    * Called by view to retrieve tree controller for the parent tree view component
    * @return tree controller
    */
   public TreeController getTree() {
      return parentTreeController;
   }

   /**
    * Called by view when edit button is clicked to open the dialog to enter a new entity
    * @param actionEvent
    */
   public void newAction(ActionEvent actionEvent) {
      log.info("newAction");

      /** retrieve new entity to edit and clone it into separate object */
      entityToEditOrig = editDataProvider.getNewEntity();
      entityToEdit = BeanUtils.cloneEntity(entityToEditOrig);

      /** populate parent selection tree with parent preselected */
      parentTreeController = new ParentTreeController<T>("Top Level", entityService,
              parentTreeUpdate, null);
      parentTreeController.setParentTreeItem(entityToEditOrig.getParent());

      /** build tree with the parent selected */
      parentTreeController.buildData();
      setInfoMessage("info_new_entity");
   }

   /**
    * Called by view when edit button is clicked to open the dialog to edit an exiting entity
    * @param actionEvent
    */
   public void editAction(ActionEvent actionEvent) {
      log.info("editAction");

      /** retrieve entity to edit and clone it into separate object */
      entityToEditOrig = editDataProvider.getEntityToEdit();
      entityToEdit = BeanUtils.cloneEntity(entityToEditOrig);

      /** populate parent selection tree with editEntity disabled and its parent selected */
      parentTreeController = new ParentTreeController<T>("Top Level", entityService,
              parentTreeUpdate, entityToEdit);
      /** build tree with the parent selected */
      parentTreeController.buildData();
      setInfoMessage("info_edit_entity");
   }

   /**
    * Called by view when save button of edit dialog is clicked
    * @param actionEvent
    */
   public void saveAction(ActionEvent actionEvent) {
      log.info("saveAction: " + entityToEdit);
      saveActionClosure.start();
   }
   /** avoid instantiation of closure object at each time */
   private EditActionClosure saveActionClosure = new EditActionClosure() {

      @Override
      protected void run() {
         doSaveAction();
      }
   };

   private void doSaveAction() {
      if (!editDataProvider.verifyEntity(entityToEdit)) {
         /** verification failed, so dialog will still be visible */
         return;
      }

      /** take over the parent entity selected in the parent tree component */
      T newParent = parentTreeController.getParentEntity();
      entityToEdit.setParent(newParent);
      log.info("entityChanged - newParent: " + newParent);

      /** if it was a new item */
      if (entityToEdit.getId() == null) {  // TODO isNew method
         entityService.save(entityToEdit);
         /** signal structural change so data can be reloaded in high level view */
         editDataProvider.relationsChanged();
      } else {
         /** otherwise it was an update action */
         log.info("entityChanged - entityToEditOrig: " + entityToEditOrig);
         T oldParent = entityToEditOrig.getParent();
         log.info("entityChanged - oldParent: " + oldParent);

         /** hand over to save the changed entity */
         entityService.updateFromCopy(entityToEditOrig, entityToEdit);

         /** if parent was changed */
         if (newParent != oldParent) {
            /** signal structural change so data can be reloaded in high level view */
            editDataProvider.relationsChanged();
         }
      }
      /** update components as provided */
      onChangeUpdateMngr.updateAll();
      /** cleanup: clear tree data and edit entity clone, this triggers the dialog to disappear */
      parentTreeController = null;
      entityToEdit = null;
      entityToEditOrig = null;
   }

   /**
    * Called by view when cancel button of dialog is clicked to abort edit dialog
    */
   public void cancelAction(ActionEvent actionEvent) {
      log.info("cancelAction");

      /** cleanup: clear tree data and edit entity clone, this triggers the dialog to disappear */
      parentTreeController = null;
      entityToEdit = null;
      entityToEditOrig = null;
   }

   /**
    * Whether edit dialog is active, means the dialog has to be displayed in view
    */
   public boolean getActive() {
      log.info("getActive: " + entityToEdit);
      return entityToEdit != null;
   }

   /**
    * Called by view when delete button for an entity is clicked
    * @param actionEvent
    */
   public void deleteAction(ActionEvent actionEvent) {
      log.info("deleteAction");

      /** check if entity may be deleted */
      entityToDelete = editDataProvider.getEntityToDelete();
      /** if it is OK to delete  */
      if (entityToDelete != null) {
         /** show confirmation dialog box */
         setConfirmMessage("confirm_delete", entityToDelete.getLabel());
      }
   }

   /**
    * Called by view when the OK button is clicked in the confirmation dialog
    * @param actionEvent
    */
   public void confirmAction(ActionEvent actionEvent) {
      log.info("confirmAction");
      confirmActionClosure.start();
   }
   /** avoid instantiation of closure object at each time */
   private EditActionClosure confirmActionClosure = new EditActionClosure() {

      @Override
      protected void run() {
         doDeleteAction();
      }
   };

   /**
    * Perform the deletion of the entity from the database
    */
   private void doDeleteAction() {
      log.info("doDeleteAction");

      if (entityToDelete != null) {
         /** the deletion was confirmed in the dialog */
         editDataProvider.performDeletion(entityToDelete);
         entityToDelete = null;
         clearMessage();
         /** update components as provided */
         onChangeUpdateMngr.updateAll();
      }
   }
}
